package mk.jug.academy.service;

import java.util.List;
import java.util.logging.Logger;

import mk.jug.academy.dao.CourseDAO;
import mk.jug.academy.dao.StudentDAO;
import mk.jug.academy.model.Course;
import mk.jug.academy.model.Lecturer;
import mk.jug.academy.model.Page;
import mk.jug.academy.model.Student;
import mk.jug.academy.model.status.CourseStatus;
import mk.jug.academy.model.status.StudentStatus;
import mk.jug.academy.preconditions.CoursePreconditions;
import mk.jug.academy.preconditions.PreconditionExceptions;
import mk.jug.academy.util.Utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;


/**
 * Provides the business logic for the {@link CourseService} contract.
 * 
 * @author Ice.Penov
 */
@Service
public class CourseServiceImpl implements CourseService {
	
	@Autowired private CourseDAO courseDAO;
	@Autowired private StudentDAO studentDAO;
		
	private static Logger LOG = Logger.getLogger(CourseServiceImpl.class.getName());
	
	@Override
	public void submitNewCourse(Course course) {
		CoursePreconditions.submitNewCourse(course);
		course.setStatus(CourseStatus.HATCHING);
		courseDAO.save(course);
		LOG.info("New course has been submitted [" + course.getTitle() + "]");
	}

	@Override
	public Page<Course> listAll(int pageNumber, int itemsPerPage) {
		return courseDAO.findAll(Utils.calculateStart(pageNumber, itemsPerPage), itemsPerPage);
	}
	
	@Override
	public Page<Course> listHatching(int pageNumber, int itemsPerPage) {
		return courseDAO.findByStatus(CourseStatus.HATCHING, Utils.calculateStart(pageNumber, itemsPerPage), itemsPerPage);
	}

	@Override
	public Page<Course> listAvailable(int pageNumber, int itemsPerPage) {
		return courseDAO.findByStatus(CourseStatus.AVAILABLE, Utils.calculateStart(pageNumber, itemsPerPage), itemsPerPage);
	}

	@Override
	public Page<Course> listInProgress(int pageNumber, int itemsPerPage) {
		return courseDAO.findByStatus(CourseStatus.IN_PROGRESS, Utils.calculateStart(pageNumber, itemsPerPage), itemsPerPage);
	}
	
	@Override
	public Page<Course> listDeactivated(int pageNumber, int itemsPerPage) {
		return courseDAO.findByStatus(CourseStatus.DEACTIVATED, Utils.calculateStart(pageNumber, itemsPerPage), itemsPerPage);
	}

	@Override
	public void hatchCourse(Course course) {
		CoursePreconditions.hatchCourse(course);
		course.setStatus(CourseStatus.AVAILABLE);
		courseDAO.save(course);
		LOG.info("Course [" + course.getTitle() + "] has been hatched");
	}	
	
	@Override
	public void startCourse(Course course) {
		CoursePreconditions.startCourse(course);
		course.setStatus(CourseStatus.IN_PROGRESS);
		courseDAO.save(course);		
		LOG.info("Course has been started [" + course.getTitle() + "]");
	}
	
	@Override
	public void finishCourse(Course course) {
		CoursePreconditions.finishCourse(course);
		course.setStatus(CourseStatus.AVAILABLE);
		courseDAO.save(course);
		LOG.info("Course has been finished [" + course.getTitle() + "]");
	}
	
	@Override
	public void deactivateCourse(Course course) {
		CoursePreconditions.deactivateCourse(course);
		course.setStatus(CourseStatus.DEACTIVATED);
		courseDAO.save(course);
		LOG.info("Course has been deactivated [" + course.getTitle() + "]");
	}
	
	@Override
	public void assignLecturer(Course course, Lecturer lecturer) {
		CoursePreconditions.assignLecturer(course, lecturer);
		course.setLecturerID(lecturer.getId());
		course.setLecturerName(lecturer.getDisplayName());
		courseDAO.save(course);
		LOG.info("Lecturer [" + lecturer.getFirstName() + " " + lecturer.getLastName() + "] has been assigned to course [" + course.getTitle() + "]");
	}
		
	@Override
	public void removeLecturer(Course course, Lecturer lecturer) {
		CoursePreconditions.removeLecturer(course, lecturer);
		course.setLecturerID(null);
		course.setLecturerName(null);
		courseDAO.save(course);
		LOG.info("Lecturer [" + lecturer.getFirstName() + " " + lecturer.getLastName() + "] has been removed from the course [" + course.getTitle() + "]");
	}
	
	@Override
	public Course load(Long id) {
		return courseDAO.find(Course.class, id);
	}	
	
	@Override
	public void saveChanges(Course course) {
		Assert.notNull(course.getId(), PreconditionExceptions.ID_IS_NULL);
		LOG.info("Course [" + course.getCode() + "] has been modified");
		courseDAO.save(course);
	}
	
	@Override @Transactional
	public void assignStudent(Course course, Student student) {
		CoursePreconditions.assignStudent(course, student);
		student.setStatus(StudentStatus.ENROLLED);
		student.setCourseID(course.getId());
		student.setCourseName(course.getTitle());
		studentDAO.save(student);
		LOG.info("Student [" + student.getFirstName() + " " + student.getLastName() +"] has been assigned to the course [" + course.getTitle() + "]");
	}
	
	@Override
	public List<Student> fetchStudentsForCourse(Course course) {
		return studentDAO.findByCourse(course.getId());
	}
	
	@Override
	public List<Course> fetchAvailable() {
		return courseDAO.findByStatus(CourseStatus.AVAILABLE);
	}
	
	@Override @Transactional
	public void removeStudent(Course course, Student student) {
		CoursePreconditions.removeStudent(course, student);
		student.setCourseID(null);
		student.setCourseName(null);
		student.setStatus(StudentStatus.WAITING_FOR_ENROLLMENT);
		LOG.info("Since the student [" + student.getFirstName() + " " + student.getLastName() +"] is not involved in courses anymore s/he is back to the WAITING_FOR_ENROLLMENT state");
		studentDAO.save(student);
	}
	
}